#include "common.h"
#include "sym.h"

#define SIZE 16384

static sym_unit table_all[SIZE]={};

Type Type_int,Type_float,Type_error;

#define sz(x) (sizeof(struct x))
#define new_type malloc(sz(Type_))
#define new_fieldlist malloc(sz(FieldList_))
#define new_typelist malloc(sz(TypeList_))
#define new_sym malloc(sz(sym_unit_))

void sym_pre_work(){
    Type_int=new_type;
    Type_int->kind=BASIC;
    Type_int->u.basic=1;
    Type_int->size=4;
    Type_int->has_fa=1;
    Type_float=new_type;
    Type_float->kind=BASIC;
    Type_float->u.basic=2;
    Type_float->size=4;
    Type_float->has_fa=1;
    Type_error=new_type;
    Type_error->kind=BASIC;
    Type_error->u.basic=3;
    Type_error->size=4;
    Type_error->has_fa=1;
    
    TypeList t=new_typelist;
    t->nxt=NULL;
    t->type=Type_int;

    sym_insert("read",func,Type_int,NULL,1,1);
    sym_insert("write",func,Type_int,t,1,1);
}

int cmp_type(Type a,Type b){
    Assert(a&&b,"Type should not be NULL!");
    if(a==Type_error||b==Type_error) return 1;
    if(a==b) return 1;
    if(a->kind!=b->kind) return 0;
    if(a->kind==BASIC) return a->u.basic==b->u.basic;
    if(a->kind==ARRAY) return cmp_type(a->u.array.elemnt,b->u.array.elemnt);
    for(FieldList x=a->u.structure,y=b->u.structure;x||y;x=x->nxt,y=y->nxt){
        if(!x||!y) return 0;
        if(!cmp_type(x->type,y->type)) return 0;
    }
    return 1;
}

int cmp_arg(TypeList a,TypeList b){
    for(;a||b;a=a->nxt,b=b->nxt){
        if(!a||!b) return 0;
        if(!cmp_type(a->type,b->type)) return 0;
    }
    return 1;
}

static int undef_func=0;
int error_no=0;
sym_unit sym_insert(char * name,int kind,Type type,TypeList arg,int dep,int valid){
    Assert(type,"TYPE should not be NULL! name=%s",name);
    Assert(kind==func||arg==NULL,"No function with arg! name=%s",name);
    Assert(dep>=1,"Unexpected dep %d with name %s",dep,name);

    static int counter=0;
    if(!name||!strlen(name)) name=str_maker("%d",++counter);

    unsigned h=hash(name);
    error_no=0;

    for(sym_unit now=table_all[h];now;now=now->nxt1) 
    if(strcmp(now->name,name)==0&&(dep==now->dep||now->kind==structdef)){
        if(kind!=now->kind||kind!=func) return NULL;
        error_no=now->valid&&valid;
        if(cmp_type(type,now->type)&&cmp_arg(now->arg,arg)){
            if(valid&&now->valid) return NULL;
            if(valid) now->valid=1,undef_func--;
            return now;
        }else {
            if(valid&&!now->valid) now->valid=1,undef_func--;
            return NULL;
        }
    }
    sym_unit x=new_sym;
    x->name=name;x->kind=kind;x->arg=arg;
    x->type=type;x->dep=dep;x->valid=valid;
    if(kind==var) x->xx=wrap_op1(new_var(),VARIABLE);
    else x->xx=NULL;
    sym_unit * to_be_insert=&table_all[h];
    while((*to_be_insert)&&(*to_be_insert)->dep>dep) to_be_insert=&(*to_be_insert)->nxt1;
    x->nxt1=*to_be_insert;*to_be_insert=x;
    x->nxt2=NULL;
    if(x->kind==func&&!valid) undef_func++;
    return x;
}

sym_unit sym_find(const char * name){
    int h=hash(name);
    for(sym_unit now=table_all[h];now;now=now->nxt1) 
    if(strcmp(now->name,name)==0) return now;
    return NULL;
}

void sym_erase(sym_unit start){
    for(sym_unit now=start,next;now;now=next){
        unsigned h=hash(now->name);
        table_all[h]=now->nxt1;
        if(!now->valid&&now->kind==var) free(now->xx);
        while(!now->type->has_fa){
            Type tp=now->type->u.array.elemnt;
            free(now->type);
            now->type=tp;
        }
        next=now->nxt2;
        free(now);
    }
    return;
}

Type arraytype_build(Type element,int size){
    Type ret=new_type;
    ret->has_fa=0;
    ret->kind=ARRAY;
    ret->u.array.elemnt=element;
    ret->u.array.size=size;
    ret->size=size*element->size;
    return ret;
}

Type struct_build(Type old,char * subname,Type subelemt){
    if(old==NULL){
        old=new_type;
        old->kind=STRUCTURE;
        old->u.structure=NULL;
        old->size=0;
        old->has_fa=1;
    }
    Assert(old->kind==STRUCTURE,"Unexpected type %d",old->kind);
    FieldList x=new_fieldlist;
    x->name=subname;
    x->type=subelemt;
    subelemt->has_fa=1;
    x->nxt=old->u.structure;
    old->u.structure=x;
    old->size+=subelemt->size;
    return old;
}

TypeList typeListBuild(Type type,TypeList nxt,int def){
    TypeList ret=new_typelist;
    ret->type=type;ret->nxt=nxt;
    if(def) type->has_fa=1;
    return ret;
}

int undef_func_detect(){return undef_func;}